/* eslint-disable @typescript-eslint/no-unused-vars */
import {
  EventHandlerUtil,
  getAttributeValueByBreakpoint,
  getObjectPropertyValueByKey,
  stringSnakeToCamel,
  throttle,
} from "../_utils/index";

export class SwapperStore {
  static store: Map<string, SwapperComponent> = new Map();

  public static set(
    instanceId: string,
    drawerComponentObj: SwapperComponent
  ): void {
    if (SwapperStore.has(instanceId)) {
      return;
    }

    SwapperStore.store.set(instanceId, drawerComponentObj);
  }

  public static get(instanceId: string): SwapperComponent | undefined {
    if (!SwapperStore.has(instanceId)) {
      return;
    }
    return SwapperStore.store.get(instanceId);
  }

  public static remove(instanceId: string): void {
    if (!SwapperStore.has(instanceId)) {
      return;
    }

    SwapperStore.store.delete(instanceId);
  }

  public static has(instanceId: string): boolean {
    return SwapperStore.store.has(instanceId);
  }

  public static getAllInstances() {
    return SwapperStore.store;
  }
}

export interface ISwapperOptions {
  mode: string;
}

export interface ISwapperQueries {
  componentName: string;
  instanseQuery: string;
  attrQuery: string;
}

const defaultSwapperOptions: ISwapperOptions = {
  mode: "append",
};

const defaultSwapperQueires: ISwapperQueries = {
  componentName: "swapper",
  instanseQuery: '[data-kt-swapper="true"]',
  attrQuery: "data-kt-swapper-",
};

class SwapperComponent {
  element: HTMLElement;
  options: ISwapperOptions;
  queries: ISwapperQueries;

  constructor(
    _element: HTMLElement,
    _options: ISwapperOptions,
    _queries: ISwapperQueries
  ) {
    this.element = _element;
    this.options = Object.assign(defaultSwapperOptions, _options);
    this.queries = _queries;

    // Initial update
    this.update();

    SwapperStore.set(this.element.id, this);
  }

  private getOption(name: string) {
    const attr = this.element.getAttribute(`${this.queries.attrQuery}${name}`);
    if (attr) {
      const value = getAttributeValueByBreakpoint(attr);
      if (attr != null && String(value) === "true") {
        return true;
      } else if (value !== null && String(value) === "false") {
        return false;
      }
      return value;
    } else {
      const optionName = stringSnakeToCamel(name);
      const option = getObjectPropertyValueByKey(this.options, optionName);
      if (option) {
        return getAttributeValueByBreakpoint(option);
      } else {
        return null;
      }
    }
  }

  ///////////////////////
  // ** Public API  ** //
  ///////////////////////
  public update = () => {
    const parentSelector = this.getOption("parent")?.toString();
    const mode = this.getOption("mode");
    const parentElement = parentSelector
      ? document.querySelector(parentSelector)
      : null;
    if (parentElement && this.element.parentNode !== parentElement) {
      const alreadyPended = document.getElementById("kt_header_menu") !== null;
      if (!alreadyPended) {
        const childEl = (parentElement as HTMLElement).querySelector(
          '[data-kt-swapper="true"]'
        );
        childEl?.remove();
        if (mode === "prepend") {
          parentElement.prepend(this.element);
        } else if (mode === "append") {
          parentElement.append(this.element);
        }
      }
    }
  };

  // Event API
  public on = (name: string, handler: Function) => {
    return EventHandlerUtil.on(this.element, name, handler);
  };

  public one = (name: string, handler: Function) => {
    return EventHandlerUtil.one(this.element, name, handler);
  };

  public off = (name: string, handlerId: string) => {
    return EventHandlerUtil.off(this.element, name, handlerId);
  };

  public trigger = (name: string, event: Event) => {
    return EventHandlerUtil.trigger(this.element, name, event);
  };

  // Static methods
  public static getInstance = (
    el: HTMLElement,
    componentName: string = defaultSwapperQueires.componentName
  ): SwapperComponent | null => {
    const place = SwapperStore.get(el.id);
    if (place) {
      return place as SwapperComponent;
    }

    return null;
  };

  public static createInstances = (
    selector: string = defaultSwapperQueires.instanseQuery,
    options: ISwapperOptions = defaultSwapperOptions,
    queries: ISwapperQueries = defaultSwapperQueires
  ) => {
    const elements = document.body.querySelectorAll(selector);
    elements.forEach((el) => {
      const item = el as HTMLElement;
      let place = SwapperComponent.getInstance(item);
      if (!place) {
        place = new SwapperComponent(item, options, queries);
      }
    });
  };

  public static createInsance = (
    selector: string = defaultSwapperQueires.instanseQuery,
    options: ISwapperOptions = defaultSwapperOptions,
    queries: ISwapperQueries = defaultSwapperQueires
  ): SwapperComponent | undefined => {
    const element = document.body.querySelector(selector);
    if (!element) {
      return;
    }
    const item = element as HTMLElement;
    let place = SwapperComponent.getInstance(item);
    if (!place) {
      place = new SwapperComponent(item, options, queries);
    }
    return place;
  };

  public static bootstrap = (
    selector: string = defaultSwapperQueires.instanseQuery
  ) => {
    SwapperComponent.createInstances(selector);
  };

  public static reinitialization = (
    selector: string = defaultSwapperQueires.instanseQuery
  ) => {
    SwapperComponent.createInstances(selector);
  };
}

// Window resize handler
window.addEventListener("resize", function () {
  let timer;
  throttle(
    timer,
    () => {
      // Locate and update Offcanvas instances on window resize
      const elements = document.querySelectorAll(
        defaultSwapperQueires.instanseQuery
      );
      elements.forEach((el) => {
        const place = SwapperComponent.getInstance(el as HTMLElement);
        if (place) {
          place.update();
        }
      });
    },
    200
  );
});

export { SwapperComponent, defaultSwapperOptions, defaultSwapperQueires };
